package com.tzu.gateway.bean.handler;

import com.tzu.gateway.bean.GatewayConfig;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetSocket;
import io.vertx.core.parsetools.RecordParser;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import thirdpart.bean.CommonMsg;


/**
 * @Author: Tzu
 * @Date: 2020/11/15 14:43
 * @Version: 1.0
 * @Description:
 */

@Log4j2
@RequiredArgsConstructor
public class ConnHandler implements Handler<NetSocket> {

    @NonNull
    private GatewayConfig config;

    //包头[ 包体长度 int + 校验和 byte + src short+ dst short + 消息类型 short + 消息状态 byte + 包编号 long ]
    private static final int PACKET_HEADER_LENGTH = 4 + 1 + 2 + 2 + 2 + 1 + 8;


    @Override
    public void handle(NetSocket socket) {

        IMsgHandler msgHandler = new MsgHandler(config.getBodyCodec());
        msgHandler.onConnect(socket);

        //1、parser：处理数据
        //定义一个解析器（初始化长度）
        RecordParser parser = RecordParser.newFixed(PACKET_HEADER_LENGTH);
        parser.setOutput(new Handler<Buffer>() {

            //    包头[ 包体长度 int + 校验和 byte + src short+ dst short + 消息类型 short + 消息状态 byte + 包编号 long ]
            int bodyLength = -1;
            byte checksum = -1;
            short msgSrc = -1;
            short msgDst = -1;
            short msgType = -1;
            byte status = -1;
            long packetNo = -1;

            @Override
            public void handle(Buffer buffer) {
                if (bodyLength == -1){
                    //① tcp流每次进来该handler方法时，首先读取包头信息
                    bodyLength = buffer.getInt(0);
                    checksum = buffer.getByte(4);
                    msgSrc = buffer.getShort(5);
                    msgDst = buffer.getShort(7);
                    msgType = buffer.getShort(9);
                    status = buffer.getByte(11);
                    packetNo = buffer.getLong(12);
                    // 动态修改解析器获取数据的长度
                    parser.fixedSizeMode(bodyLength);
                }else {
                    //② 读取包体数据（orderCmd对象的序列化信息）
                    byte[] bodyBytes = buffer.getBytes();
                    //组装对象
                    CommonMsg msg;
                    //在组装CommonMsg对象前，做一些判断
                    if (checksum != config.getCs().getChecksum(bodyBytes)){
                        //包体被篡改：丢弃此包
                        log.error("illegal byte body exist from client:{}",socket.remoteAddress());
                        return;
                    }else {
                        if (msgDst != config.getId()){
                            //包发错地方：丢弃
                            log.error("recv error msgDst dst:{} from client:{}",msgDst,socket.remoteAddress());
                            return;
                        }
                    }
                    //开始组装对象
                    msg = new CommonMsg();//创建模板
                    msg.setBodyLength(bodyBytes.length);
                    msg.setChecksum(checksum);
                    msg.setMsgSrc(msgSrc);
                    msg.setMsgDst(msgDst);
                    msg.setMsgType(msgType);
                    msg.setStatus(status);
                    msg.setMsgNo(packetNo);
                    msg.setBody(bodyBytes);
                    msg.setTimestamp(System.currentTimeMillis());

                    //处理数据: 把msg对象里的body属性信息反序列化成orderCmd,并缓存orderCmd到内存中
                    msgHandler.onCounterData(msg);

                    //恢复现场：以便读取下一个包
                    bodyLength = -1;
                    checksum = -1;
                    msgSrc = -1;
                    msgDst = -1;
                    msgType = -1;
                    status = -1;
                    packetNo = -1;
                    parser.fixedSizeMode(PACKET_HEADER_LENGTH);

                }
            }
        });
        //与当前的socket绑定
        socket.handler(parser);

        //2、关闭处理器
        socket.closeHandler(close -> {
            msgHandler.onDisConnect(socket);
        });

        //3、异常
        socket.exceptionHandler(e -> {
            msgHandler.onException(socket,e);
            socket.close();
        });
    }
}
